@@ -11,7 +11,9 @@ module Agents |
||
11 | 11 |
description <<-MD |
12 | 12 |
|
13 | 13 |
The ImapFolderAgent checks an IMAP server in specified folders |
14 |
- and creates Events based on new unread mails. |
|
14 |
+ and creates Events based on new mails found since the last run. |
|
15 |
+ In the first visit to a foler, this agent only checks for the |
|
16 |
+ initial status and does not create events. |
|
15 | 17 |
|
16 | 18 |
Specify an IMAP server to connect with `host`, and set `ssl` to |
17 | 19 |
true if the server supports IMAP over SSL. Specify `port` if |
@@ -65,6 +67,13 @@ module Agents |
||
65 | 67 |
body. The default value is `['text/plain', 'text/enriched', |
66 | 68 |
'text/html']`. |
67 | 69 |
|
70 |
+ - "is_unread" |
|
71 |
+ |
|
72 |
+ Setting this to true or false means only mails that is |
|
73 |
+ marked as unread or read respectively, are selected. |
|
74 |
+ |
|
75 |
+ If this key is unspecified or set to null, it is ignored. |
|
76 |
+ |
|
68 | 77 |
- "has_attachment" |
69 | 78 |
|
70 | 79 |
Setting this to true or false means only mails that does or does |
@@ -77,11 +86,13 @@ module Agents |
||
77 | 86 |
Each agent instance memorizes the highest UID of mails that are |
78 | 87 |
found in the last run for each watched folder, so even if you |
79 | 88 |
change a set of conditions so that it matches mails that are |
80 |
- missed previously, or if you unmark already seen mails as read, |
|
81 |
- they will not show up as new events. Also, in order to avoid |
|
82 |
- duplicated notification it keeps a list of Message-Id's of 100 |
|
83 |
- most recent mails, so if multiple mails of the same Message-Id |
|
84 |
- are found, you will only see one event out of them. |
|
89 |
+ missed previously, or if you alter the flag status of already |
|
90 |
+ found mails, they will not show up as new events. |
|
91 |
+ |
|
92 |
+ Also, in order to avoid duplicated notification it keeps a list |
|
93 |
+ of Message-Id's of 100 most recent mails, so if multiple mails |
|
94 |
+ of the same Message-Id are found, you will only see one event |
|
95 |
+ out of them. |
|
85 | 96 |
MD |
86 | 97 |
|
87 | 98 |
event_description <<-MD |
@@ -200,7 +211,7 @@ module Agents |
||
200 | 211 |
errors.add(:base, 'conditions.%s contains a non-string object' % key) |
201 | 212 |
end |
202 | 213 |
} |
203 |
- when 'has_attachment' |
|
214 |
+ when 'is_unread', 'has_attachment' |
|
204 | 215 |
case boolify(value) |
205 | 216 |
when true, false |
206 | 217 |
else |
@@ -258,6 +269,8 @@ module Agents |
||
258 | 269 |
} |
259 | 270 |
when 'has_attachment' |
260 | 271 |
boolify(value) == mail.has_attachment? |
272 |
+ when 'is_unread' |
|
273 |
+ true # already filtered out by each_unread_mail |
|
261 | 274 |
else |
262 | 275 |
log 'Unknown condition key ignored: %s' % key |
263 | 276 |
true |
@@ -321,25 +334,47 @@ module Agents |
||
321 | 334 |
imap.select(folder) |
322 | 335 |
uidvalidity = imap.uidvalidity |
323 | 336 |
|
324 |
- if lastseenuid = lastseen[uidvalidity] |
|
325 |
- seen[uidvalidity] = lastseenuid |
|
326 |
- uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS'). |
|
327 |
- each_with_object([]) { |data, ret| |
|
328 |
- uid, flags = data.attr.values_at('UID', 'FLAGS') |
|
329 |
- seen[uidvalidity] = uid |
|
330 |
- next if uid <= lastseenuid || flags.include?(:Seen) |
|
331 |
- ret << uid |
|
332 |
- } |
|
333 |
- else |
|
334 |
- uids = imap.uid_search('UNSEEN') |
|
335 |
- seen[uidvalidity] = uids.max unless uids.empty? |
|
336 |
- end |
|
337 |
+ lastseenuid = lastseen[uidvalidity] |
|
338 |
+ |
|
339 |
+ if lastseenuid.nil? |
|
340 |
+ maxseq = imap.responses['EXISTS'].last |
|
341 |
+ |
|
342 |
+ log "Recording the initial status: %s" % pluralize(maxseq, 'existing mail') |
|
343 |
+ |
|
344 |
+ if maxseq > 0 |
|
345 |
+ seen[uidvalidity] = imap.fetch(maxseq, 'UID').last.attr['UID'] |
|
346 |
+ end |
|
337 | 347 |
|
338 |
- if uids.empty? |
|
339 |
- log "No unread mails" |
|
340 | 348 |
next |
341 | 349 |
end |
342 | 350 |
|
351 |
+ seen[uidvalidity] = lastseenuid |
|
352 |
+ is_unread = boolify(interpolated['conditions']['is_unread']) |
|
353 |
+ |
|
354 |
+ uids = imap.uid_fetch((lastseenuid + 1)..-1, 'FLAGS'). |
|
355 |
+ each_with_object([]) { |data, ret| |
|
356 |
+ uid, flags = data.attr.values_at('UID', 'FLAGS') |
|
357 |
+ seen[uidvalidity] = uid |
|
358 |
+ next if uid <= lastseenuid |
|
359 |
+ |
|
360 |
+ case is_unread |
|
361 |
+ when nil, !flags.include?(:Seen) |
|
362 |
+ ret << uid |
|
363 |
+ end |
|
364 |
+ } |
|
365 |
+ |
|
366 |
+ log pluralize(uids.size, |
|
367 |
+ case is_unread |
|
368 |
+ when true |
|
369 |
+ 'new unread mail' |
|
370 |
+ when false |
|
371 |
+ 'new read mail' |
|
372 |
+ else |
|
373 |
+ 'new mail' |
|
374 |
+ end) |
|
375 |
+ |
|
376 |
+ next if uids.empty? |
|
377 |
+ |
|
343 | 378 |
imap.uid_fetch_mails(uids).each { |mail| |
344 | 379 |
yield mail, notified |
345 | 380 |
} |
@@ -391,6 +426,10 @@ module Agents |
||
391 | 426 |
File.fnmatch?(pattern, value, FNM_FLAGS) |
392 | 427 |
end |
393 | 428 |
|
429 |
+ def pluralize(count, noun) |
|
430 |
+ "%d %s" % [count, noun.pluralize(count)] |
|
431 |
+ end |
|
432 |
+ |
|
394 | 433 |
class Client < ::Net::IMAP |
395 | 434 |
class << self |
396 | 435 |
def open(host, port, ssl) |